﻿namespace MessageRouter
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Collections.ObjectModel;
    using MessageRouter.Configuration;
    using System.Diagnostics;

    public class RouterHost : IDisposable
    {
        private readonly Dictionary<string, MessageReader> readers;
        private readonly Dictionary<string, MessageWriter> writers;
        private readonly Collection<IDisposable> subscriptions;

        public RouterHost()
        {
            this.readers = new Dictionary<string, MessageReader>();
            this.writers = new Dictionary<string, MessageWriter>();
            this.subscriptions = new Collection<IDisposable>();

            // create reader instances
            foreach (ReaderConfigurationElement readerConfig in ConfigurationManager.Current.Readers)
            {
                MessageReader reader = (MessageReader)Activator.CreateInstance(readerConfig.Type, new object[] { readerConfig });
                this.readers.Add(reader.Name, reader);
            }

            // create writer instances
            foreach (WriterConfigurationElement writerConfig in ConfigurationManager.Current.Writers)
            {
                MessageWriter writer = (MessageWriter)Activator.CreateInstance(writerConfig.Type, new object[] { writerConfig });
                this.writers.Add(writer.Name, writer);
            }
        }

        public void Open()
        {
            try
            {
                // create subscriptions
                foreach (BindingConfigurationElement subscriptionConfig in ConfigurationManager.Current.Subscriptions)
                {
                    var reader = this.readers[subscriptionConfig.ReaderName];
                    var writer = this.writers[subscriptionConfig.WriterName];
                    IDisposable subscription = reader.Subscribe(writer);
                    writer.InSubscription = true;
                    subscriptions.Add(subscription);
                }
                // open subsscribed writers
                foreach (var writer in this.writers.Values.Where(h => h.InSubscription))
                {
                    writer.Open();
                }
                // open readers has subscribers
                foreach (var reader in this.readers.Values.Where(h => h.HasSubscribers))
                {
                    reader.Open();
                }

                Trace.TraceInformation("RouterHost opened");
            }
            catch (Exception ex)
            {
                Trace.TraceInformation("RouterHost failed with error: {0}", ex.Message);
                throw;
            }
        }

        public void Close()
        {
            // Dispose subscriptions
            foreach (IDisposable subscription in this.subscriptions)
            {
                subscription.Dispose();
            }
            // Close writers
            MessageWriter[] writers = this.writers.Values.ToArray();
            foreach (MessageWriter writer in writers)
            {
                writer.Close();
            }
            // Close readers
            MessageReader[] readers = this.readers.Values.ToArray();
            foreach (MessageReader reader in readers)
            {
                reader.Close();
            }
            Trace.TraceInformation("RouterHost closed");
        }

        private bool disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    this.Close();
                }
                this.disposed = true;
            }
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~RouterHost()
        {
            this.Dispose(false);
        }
    }
}
